
#ifndef __BLOCK_WRITE_SERVER_H
#define __BLOCK_WRITE_SERVER_H

#include <stdint.h>
#include <apr.h>
#include <apr_atomic.h>
#include "UDPServer.h"

#define PACKET_BLOCK_SIZE   0x000400 // 1024 bytes
#define HASHING_BLOCK_SIZE (0x000400 * 0x80) // 1024 bytes * 128 = 128KiB
// multiplied by bit length of SHA-1 hash to make sending the fill bit
// vector equivalent size to the hashes.
#define ACTIVE_BLOCK_SIZE   0x800000 // 8MiB
// Active blocks, due to the representation in the UDP blocks, have a
// maximum size of 64Mb, or ((ACTIVE_BLOCK_SIZE / PACKET_BLOCK_SIZE)
// == 65536)
#define SHA1_DIGEST_LENGTH  (0xA0 / 0x08)

/** This structure holds a number of packets whose contents are copied
 * in. When the structure is filled the contents are checksummed
 * (hashed).
 * 
 * @param hashblockdata The main data structure that holds all the
 * data. This is taken from packets by being memcpy'd into the
 * appropriate place.
 * 
 * @param checksum Once the main data structure is filled, the
 * checksum (hash) goes here.
 * 
 * @param filled_bit_vector As each packet is copied in, a bit is
 * turned on in this bit vector atomically so it does not overwrite
 * data.
 *
 * @param committedblocks The number of commited packet blocks is
 * written here. As this is incremented accurately with each packet,
 * this is a true representation and can be queried first to determine
 * if this structure is writable. */
typedef struct blockhashblock
{
   uint32_t hashblockdata[HASHING_BLOCK_SIZE / sizeof(uint32_t)];
   uint8_t checksum[SHA1_DIGEST_LENGTH];
   uint32_t filled_bit_vector[(HASHING_BLOCK_SIZE / PACKET_BLOCK_SIZE) / (sizeof(uint32_t) * 8)];
   uint32_t committedblocks;
} BlockHashBlock;

/** This structure holds an active slot. An active slot is a
 * collection of up to ACTIVE_BLOCK_SIZE bytes, partitioned into many
 * hash blocks, that are each verified by comparison to hashes held
 * here. On verification, the blocks are copied back to the file.
 * 
 * @param localmp The local memory pool for this slot, so that once
 * the slot is completed or forgotten all memory can be released
 * easily.
 * 
 * @param filename The filename of the file that is being transferred
 * into.
 * 
 * @param offset The offset of the active block being worked on by
 * this slot inside the file.
 * 
 * @param blockid The ID of the block being worked on by this
 * slot. Any packet wanting to deposit a payload inside the active
 * block must have this ID in its header.
 * 
 * @param active Whether this slot is to be considered live. This is
 * the first variable queried before deciding whether to add a packet
 * into the structure, or when looking for a new slot to activate
 * (reusing the memory).
 * 
 * @param blocklength Often the block length is less than
 * ACTIVE_BLOCK_SIZE, so a block length must be set. This allows for
 * less padding, and no zero packets to need to be sent in order for a
 * slot to complete.
 * 
 * @param verified_bit_vector A bit vector wherein each bit represents
 * one hashed block. If the hashed block has been matched successfully
 * and written back to the file, the corresponding bit is set.
 * 
 * @param hashes An array containing the list of hashes for each of
 * the hashed blocks in turn.
 * 
 * @param hashblock Structures containing the hashed blocks. */
typedef struct blockactiveblock
{
   apr_pool_t *localmp;
   char *filename;
   uint64_t offset;
   uint32_t blockid;
   uint32_t active;
   uint32_t blocklength;
   uint32_t packetcounter;
   uint32_t verified_bit_vector[(ACTIVE_BLOCK_SIZE / HASHING_BLOCK_SIZE) / (sizeof(uint32_t) * 8)];
   uint8_t *hashes;
   BlockHashBlock hashblock[ACTIVE_BLOCK_SIZE / HASHING_BLOCK_SIZE];
} BlockActiveBlock;

#define BLOCK_WRITE_SERVER__SUCCESS                 0
#define BLOCK_WRITE_SERVER__ALL_SLOTS_IN_USE        1
#define BLOCK_WRITE_SERVER__REQUESTED_BLOCK_TOO_BIG 2
#define BLOCK_WRITE_SERVER__FILE_UNALLOCATED        3
#define BLOCK_WRITE_SERVER__BLOCK_BEYOND_FILE_END   4
#define BLOCK_WRITE_SERVER__FILE_READ_ERROR         5
#define BLOCK_WRITE_SERVER__FILE_WRITE_ERROR        6
#define BLOCK_WRITE_SERVER__FILE_SEEK_ERROR         7
#define BLOCK_WRITE_SERVER__MEMORY_ERROR            8
#define BLOCK_WRITE_SERVER__FILE_NAME_ERROR         9
#define BLOCK_WRITE_SERVER__INVALID_BLOCK_ID        10

/** Server for writing UDP blocks to files. Does not have to have
 * reliability to function. */
class BlockWriteServer : public UDPServer
{
   public:
      BlockWriteServer(int inoofthreads, int iportno, const char *ihostname, apr_pool_t *upperpool, int irportno, const char *irhostname, int inomemfileblocks);
      ~BlockWriteServer();
      void handleClientsOnThread(int threadid, apr_pool_t *localmp);
      static void *handleClientsOnThreadStatic(apr_thread_t *tthread, void *args);
      /** Get the client handler as a function pointer. */
      virtual functor getClientHandlerFunction() const { return (functor)(&handleClientsOnThreadStatic); }
      void watchFromThread(apr_pool_t *localmp);
      static void *watchFromThreadStatic(apr_thread_t *tthread, void *args);
      /** Get the watch thread as a function pointer. */
      virtual functor getWatchFromThreadFunction() const { return (functor)(&watchFromThreadStatic); }
      int startFileBlock(const char *filename, uint64_t offset, int64_t block_length, char *goodhashes, uint32_t *blockid_result);
      int stopFileBlock(uint32_t stopslot);
      uint32_t *checkFileBlock(uint32_t slotID);
      uint32_t *getActiveBlockFillVector(uint32_t blockID);
      int commitFileBlock(const char *filename, int64_t offset, const char *data, int datalength, apr_pool_t *t_mp);
      void writePacket(uint16_t blockID, uint16_t block_offset, const char *packetdata);
      int startWatchThread();
      int joinWatchThread();
      /** Array of active blocks / active slots. */
      BlockActiveBlock *blockarray;
      /** The watch thread. */
      apr_thread_t *watch_thread;
      /** The thread attributes for the watch thread. */
      apr_threadattr_t *watch_threadattr;
   private:
      void calculateCompletedHashedBlock(uint32_t slotID, uint32_t hashblockid);
      /** Number of slots in the array of possible active slots. */
      int nomemfileblocks;
      /** Stop variable for the watch thread. */
      int watchthreadstop;
      /** Remote port number. */
      int rportno;
      /** Remove host name. */
      char *rhostname;
      /** Lock for incrementing the block index for defining the
       * blocks. */
      APRMutex *blockinclock;
      APRMutex *watchthread;
      /** Lock for file I/O. */
      APRMutex *fileslock;
      /** Variable which provides unique block indexes. */
      uint32_t incidx;
};

#endif
